package com.ruoyi.web.controller.user;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.ValidateCodeUtils.ValidateCodeUtil;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.domain.config.VsDramasConfig;
import com.ruoyi.system.domain.config.VsDramasRichtext;
import com.ruoyi.system.domain.reseller.VsDramasReseller;
import com.ruoyi.system.domain.reseller.VsDramasResellerBind;
import com.ruoyi.system.domain.reseller.VsDramasResellerUser;
import com.ruoyi.system.domain.task.VsDramasTask;
import com.ruoyi.system.domain.task.VsDramsTaskLog;
import com.ruoyi.system.domain.user.VsDramasUserWalletLog;
import com.ruoyi.system.mapper.config.VsDramasRichtextMapper;
import com.ruoyi.system.mapper.task.VsDramasTaskMapper;
import com.ruoyi.system.mapper.user.VsDramasUserWalletLogMapper;
import com.ruoyi.system.service.config.IVsDramasConfigService;
import com.ruoyi.system.service.reseller.IVsDramasResellerBindService;
import com.ruoyi.system.service.reseller.IVsDramasResellerService;
import com.ruoyi.system.service.reseller.IVsDramasResellerUserService;
import com.ruoyi.system.service.task.IVsDramsTaskLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import com.ruoyi.framework.web.service.TokenService;
import org.springframework.stereotype.Service;
import com.ruoyi.system.mapper.user.VsUserMapper;
import com.ruoyi.common.core.domain.model.VsUser;
import com.ruoyi.system.service.user.IVsUserService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
/**
 * This file is part of 南阳迈特网络科技有限公司.
 * @company  南阳迈特网络科技有限公司
 * @link     https://www.nymaite.com
 * @document https://product.nymaite.cn
 * @contact  service@nymaite.com
 * @author nymaite
 */
/**
 * 会员Service业务层处理
 * 因为调用了其他包的代码，如果还放原来的位置会导致循环依赖，节省不必要的解决功能时间，所以暂时放这了
 * @author zzx
 * @date 2024-04-30
 */
@Service
public class VsUserServiceImpl implements IVsUserService 
{
    @Autowired
    private VsUserMapper vsUserMapper;

    @Resource
    @Qualifier("VsUserAuthenticationManager")
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private VsDramasTaskMapper vsDramasTaskMapper;

    @Autowired
    private VsDramasUserWalletLogMapper vsDramasUserWalletLogMapper;

    @Autowired
    private VsDramasRichtextMapper vsDramasRichtextMapper;

    @Autowired
    private IVsDramasConfigService vsDramasConfigService;

    @Autowired
    private IVsDramsTaskLogService vsDramsTaskLogService;

    @Autowired
    private IVsDramasResellerUserService vsDramasResellerUserService;

    @Autowired
    private IVsDramasResellerBindService vsDramasResellerBindService;

    @Autowired
    private IVsDramasResellerService vsDramasResellerService;

    /**
     * 查询会员
     * 
     * @param id 会员主键
     * @return 会员
     */
    @Override
    public VsUser selectVsUserById(Long id)
    {
        return vsUserMapper.selectVsUserById(id);
    }

    @Override
    public VsUser selectVsUserByInfo(VsUser vsUser) {
        VsUser user = vsUserMapper.selectVsUserByInfo(vsUser);
        if (user == null) {
            return null;
        }
        VsDramasResellerBind bind = new VsDramasResellerBind();
        bind.setUserId(user.getId());
        List<VsDramasResellerBind> userBinds = vsDramasResellerBindService.selectVsDramasResellerBindList(bind);
        if (!userBinds.isEmpty()) {
            Long expiretime = userBinds.get(0).getExpiretime();
            if (expiretime != null && expiretime > System.currentTimeMillis()) {
                user.setResellerExpiretime(expiretime);
                Map<String, String> map = JSON.parseObject(userBinds.get(0).getResellerJson(), new TypeReference<Map<String, String>>(){});
                user.setResellerName(map.get("name"));
            } else if (expiretime != null && expiretime == 0) {
                user.setResellerExpiretime(0L);
                Map<String, String> map = JSON.parseObject(userBinds.get(0).getResellerJson(), new TypeReference<Map<String, String>>(){});
                user.setResellerName(map.get("name"));
            }
        }
        return user;
    }

    /**
     * 查询会员列表
     * 
     * @param vsUser 会员
     * @return 会员
     */
    @Override
    public List<VsUser> selectVsUserList(VsUser vsUser)
    {
        return vsUserMapper.selectVsUserList(vsUser);
    }

    /**
     * 新增会员
     * 
     * @param vsUser 会员
     * @return 结果
     */
    @Override
    public int insertVsUser(VsUser vsUser)
    {
        return vsUserMapper.insertVsUser(vsUser);
    }

    /**
     * 修改会员
     * 
     * @param vsUser 会员
     * @return 结果
     */
    @Override
    public int updateVsUser(VsUser vsUser)
    {
        if (vsUser.getPassword() != null && !vsUser.getPassword().isEmpty()) {
            vsUser.setPassword(SecurityUtils.encryptPassword(vsUser.getPassword()));
        }
        return vsUserMapper.updateVsUser(vsUser);
    }

    /**
     * 批量删除会员
     * 
     * @param ids 需要删除的会员主键
     * @return 结果
     */
    @Override
    public int deleteVsUserByIds(Long[] ids)
    {
        return vsUserMapper.deleteVsUserByIds(ids);
    }

    /**
     * 删除会员信息
     * 
     * @param id 会员主键
     * @return 结果
     */
    @Override
    public int deleteVsUserById(Long id)
    {
        return vsUserMapper.deleteVsUserById(id);
    }

    @Override
    public Map login(VsUser vsUser) {
        // 生成令牌
        Authentication authentication;
        try {
            // 原来其实就这么一句话：该方法会去调用UserDetailsServiceImpl.loadUserByUsername。指的是原来若依自定义的UserDetailsServiceImpl
            //此处会让人很迷惑，特别是对新手来说。其实就是调用了AppUserDetailsServiceImpl中的loadUserByUsername方法
            //而这个方法的是通过AppAuthenticationProvider中去发起的。所以这个authenticationManager  其实就是注入的AppAuthenticationProvider
            //这个地方一定要注意！！！！！
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(vsUser.getKeyword(), vsUser.getPassword()));
        } catch (Exception e) {
            if (e instanceof BadCredentialsException) {
                throw new UserPasswordNotMatchException();
            } else {
                throw new ServiceException(e.getMessage());
            }
        }

        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        String token = tokenService.createToken(loginUser);

        updateLogin(loginUser.getVsUser());
        VsUser user = vsUserMapper.selectVsUserById(loginUser.getVsUser().getId());
        loginUser.setVsUser(user);
        // 生成token
        HashMap<String, Object> map = new HashMap<>();
        map.put("token",token);
        map.put("user",loginUser.getVsUser());
        return map;
    }

    @Override
    public Map smsLogin(VsUser vsUser) {
        // 生成令牌
//        Authentication authentication;
//        try {
//            // 原来其实就这么一句话：该方法会去调用UserDetailsServiceImpl.loadUserByUsername。指的是原来若依自定义的UserDetailsServiceImpl
//            //此处会让人很迷惑，特别是对新手来说。其实就是调用了AppUserDetailsServiceImpl中的loadUserByUsername方法
//            //而这个方法的是通过AppAuthenticationProvider中去发起的。所以这个authenticationManager  其实就是注入的AppAuthenticationProvider
//            //这个地方一定要注意！！！！！
//
//            authentication = authenticationManager
//                    .authenticate(new UsernamePasswordAuthenticationToken(vsUser.getKeyword(), null));
//        } catch (Exception e) {
//            if (e instanceof BadCredentialsException) {
//                throw new UserPasswordNotMatchException();
//            } else {
//                throw new ServiceException(e.getMessage());
//            }
//        }

        LoginUser loginUser = new LoginUser();
        loginUser.setVsUser(vsUser);
        String token = tokenService.createToken(loginUser);

        updateLogin(loginUser.getVsUser());
        VsUser user = vsUserMapper.selectVsUserById(loginUser.getVsUser().getId());
        if (!StringUtils.isEmpty(vsUser.getRandomPwd())) {
            user.setRandomPwd(vsUser.getRandomPwd());
        }
        loginUser.setVsUser(user);
        // 生成token
        HashMap<String, Object> map = new HashMap<>();
        map.put("token",token);
        map.put("user",loginUser.getVsUser());
        return map;
    }

    private void updateLogin(VsUser user) {
        if (user.getPrevtime() == null) {
            user.setPrevtime(System.currentTimeMillis());
        } else {
            user.setPrevtime(System.currentTimeMillis());
        }
        user.setLoginip(IpUtils.getIpAddr(ServletUtils.getRequest()));
        user.setLogintime(System.currentTimeMillis());
        vsUserMapper.updateVsUser(user);
    }
    @Transactional
    @Override
    public VsUser registerUser(VsUser vsUser, String langId) {
        VsUser info = null;
        if (vsUser.getKeyword() != null && !vsUser.getKeyword().isEmpty()) {
            info = vsUserMapper.selectVsUserInfo(vsUser.getKeyword());
        }
        if (info != null) {
            return info;
        }
        VsDramasConfig vsDramasConfig = new VsDramasConfig();
        vsDramasConfig.setName("user");
        List<VsDramasConfig> configs = vsDramasConfigService.selectVsDramasConfigList(vsDramasConfig);
        String s = null;
        if (vsUser.getOpenid() != null && !vsUser.getOpenid().isEmpty()) {
            VsUser guest = new VsUser();
            guest.setPassword(vsUser.getOpenid());
            VsUser guestInfo = vsUserMapper.selectVsUserByInfo(guest);
            if (guestInfo != null) {
                return guestInfo;
            }
            vsUser.setPassword(vsUser.getOpenid());
        }
        if (StringUtils.isEmpty(vsUser.getPassword())) {
            s = IdUtils.fastSimpleUUID();
            vsUser.setRandomPwd(s);
            vsUser.setPassword(SecurityUtils.encryptPassword(s));
        }
        if (StringUtils.isEmpty(vsUser.getKeyword()) && vsUser.getKeyword() == null && StringUtils.isEmpty(vsUser.getProvider())) {
            vsUser.setMode("guest");
        } else {
            if (!StringUtils.isEmpty(vsUser.getKeyword()) && vsUser.getKeyword() != null) {
                vsUser.setMode("user");
                String regex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
                Pattern pattern = Pattern.compile(regex);
                Matcher matcher = pattern.matcher(vsUser.getKeyword());
                // 如果符合表达式则代表keyword是邮箱，否则是手机号
                if (matcher.matches()) {
                    vsUser.setEmail(vsUser.getKeyword());
                } else {
                    vsUser.setMobile(vsUser.getKeyword());
                }
            }
        }
        vsUser.setStatus("normal");
        vsUser.setMoney(BigDecimal.valueOf(0));
        vsUser.setScore(0L);
        vsUser.setUsable(0L);
        vsUser.setSuccessions("0");
        vsUser.setMaxsuccessions("0");
        vsUser.setLoginfailure("0");
        vsUser.setJoinip(IpUtils.getIpAddr(ServletUtils.getRequest()));
        vsUser.setJointime(System.currentTimeMillis());
        vsUser.setCreatetime(System.currentTimeMillis());
        Integer code = ValidateCodeUtil.generateValidateCode(4);
        if (!configs.isEmpty()) {
            JSONObject config = JSONObject.parseObject(configs.get(0).getValue());
            vsUser.setAvatar(config.getString("avatar"));
            vsUser.setUsername(config.getString("nickname")+code);
            vsUser.setNickname(config.getString("nickname")+code);
        } else {
            vsUser.setAvatar("");
            vsUser.setUsername("Mettshort-"+code);
            vsUser.setNickname("Mettshort-"+code);
        }
        if (vsUser.getSpm() != null && !vsUser.getSpm().isEmpty()) {
            String result = vsUser.getSpm().split("\\.")[0];
            vsUser.setParentUserId(Long.valueOf(result));
        }
        int i = vsUserMapper.insertVsUser(vsUser);
        if (i > 0) {
            info = vsUserMapper.selectVsUserById(vsUser.getId());
            info.setRandomPwd(s);
            // 注册成功之后再去赠送新用户注册的积分
//            if (vsUser.getParentUserId() != null) {
                usable(info, langId);
//            }
        }
        return info;
    }

    // 用户不一定是通过别人邀请而注册的，所以还是把代码提取出来吧
    private void usable(VsUser vsUser, String langId){
        VsDramasTask task = new VsDramasTask();
        task.setLangId(Integer.valueOf(langId));
        List<VsDramasTask> taskList = vsDramasTaskMapper.selectVsDramasTaskList(task);
        if (!taskList.isEmpty()) {
            // 定义两个值防止有重名任务事件导致重复给用户赠送积分
            // 如果等于1则代表已经赠送过积分，有重复事件也不会再次触发
            int register = 0;
            int share = 0;
            for (VsDramasTask t : taskList) {
                if ("user_register_after".equals(t.getHook()) && register < 1) {
                    register++;
                    vsUser.setUsable(Long.valueOf(t.getUsable()));
                    VsDramsTaskLog log = new VsDramsTaskLog();
                    log.setUserId(vsUser.getId());
                    log.setTaskId(Long.valueOf(t.getId()));
                    log.setCreatetime(System.currentTimeMillis());
                    vsDramsTaskLogService.insertVsDramsTaskLog(log);
                }
                // 先判断当前用户是不是被邀请注册的，如果是就给他上级用户赠送邀请新用户任务对应的积分
                if (vsUser.getParentUserId() != null) {
                    // 判断是否是被邀请注册的，如果是就添加到分销用户关系表中
                    VsUser parentUser = vsUserMapper.selectVsUserById(vsUser.getParentUserId());
                    if (parentUser != null) {
                        VsDramasResellerUser resellerUser = new VsDramasResellerUser();
                        resellerUser.setUserId(vsUser.getId());
                        resellerUser.setParentId(parentUser.getId());
                        resellerUser.setResellerUserId(Math.toIntExact(parentUser.getId()));
                        resellerUser.setType("1");
                        resellerUser.setCreatetime(System.currentTimeMillis());
                        vsDramasResellerUserService.insertVsDramasResellerUser(resellerUser);
                        // 如果邀请者也有上级的话那就也添加到分销用户关系表中
                        if (parentUser.getParentUserId() != null) {
                            VsDramasResellerUser parentResellerUser = new VsDramasResellerUser();
                            parentResellerUser.setUserId(vsUser.getId());
                            parentResellerUser.setParentId(parentUser.getId());
                            parentResellerUser.setResellerUserId(Math.toIntExact(parentResellerUser.getId()));
                            parentResellerUser.setType("2");
                            parentResellerUser.setCreatetime(System.currentTimeMillis());
                            vsDramasResellerUserService.insertVsDramasResellerUser(parentResellerUser);
                        }
                    }
                    if ("share_success".equals(t.getHook()) && share < 1) {

                        VsDramsTaskLog taskLog = new VsDramsTaskLog();
                        taskLog.setUserId(vsUser.getParentUserId());
                        taskLog.setTaskId(Long.valueOf(t.getId()));
                        // 如果任务不是一次性的就要查每天的任务数
                        if ("day".equals(t.getType())) {
                            taskLog.setStarttime(TimeStampUtils.getStartTimeStamp());
                            taskLog.setEndtime(TimeStampUtils.getEndTimeStamp());
                        }
                        List<VsDramsTaskLog> taskLogs = vsDramsTaskLogService.selectVsDramsTaskLogList(taskLog);
                        // 达到上限则不做任何操作，未到上限再去赠送任务对应积分额度
                        if (taskLogs.size() < t.getLimit()) {
                            share++;
                            // 如果没有达到任务上限则保存用户任务记录，成功之后再去给对应用户赠送积分
                            VsDramsTaskLog log = new VsDramsTaskLog();
                            log.setUserId(vsUser.getParentUserId());
                            log.setTaskId(Long.valueOf(t.getId()));
                            log.setCreatetime(System.currentTimeMillis());
                            vsDramsTaskLogService.insertVsDramsTaskLog(log);
                            Integer usable = t.getUsable();
                            VsDramasUserWalletLog walletLog = new VsDramasUserWalletLog();
                            walletLog.setUserId(vsUser.getParentUserId());
                            walletLog.setWallet(BigDecimal.valueOf(usable));
                            walletLog.setWalletType("usable");
                            walletLog.setType("task");
                            walletLog.setBefore(BigDecimal.valueOf(parentUser.getUsable()));
                            long l = parentUser.getUsable() + Long.valueOf(usable);
                            walletLog.setAfter(BigDecimal.valueOf(l));
                            walletLog.setMemo(t.getTitle());
                            walletLog.setOperType(parentUser.getMode());
                            walletLog.setOperId(parentUser.getId());
                            walletLog.setCreatetime(System.currentTimeMillis());
                            walletLog.setUpdatetime(System.currentTimeMillis());
                            vsDramasUserWalletLogMapper.insertVsDramasUserWalletLog(walletLog);
                        }
                    }
                }
            }
        }
    }

    @Override
    public VsUser selectVsUserInfo(VsUser vsUser) {
        return vsUserMapper.selectVsUserInfo(vsUser.getKeyword());
    }

    @Override
    public void checkCode(String keyword, String code) {
        if (StringUtils.isBlank(keyword) || StringUtils.isBlank(code)) {
            throw new RuntimeException("手机号或验证码不能为空");
        }
        String regex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(keyword);
        // 如果符合表达式则代表keyword是邮箱，否则是手机号
        String key;
        if (matcher.matches()) {
            key = CacheConstants.EMS_CODE_KEY + keyword;
        } else {
            key = CacheConstants.SMS_CODE_KEY + keyword;
        }

        redisCache.redisTemplate.setKeySerializer(new StringRedisSerializer());

        if (!redisCache.hasKey(key)) {
            throw new RuntimeException("验证码不存在");
        }

        Object codeObj = redisCache.getCacheObject(key);
        if (codeObj == null) {
            throw new RuntimeException("验证码已过期");
        }
        String saveCode = codeObj.toString();
        if (StringUtils.isBlank(saveCode)) {
            redisCache.deleteObject(key);
            throw new RuntimeException("未找到验证码");
        }
        if (!StringUtils.equals(saveCode, code)) {
            throw new RuntimeException("验证码不正确");
        }
        redisCache.deleteObject(key);
    }

    @Override
    public String sendEmail(String email, String langId) {
        String verifyKey = CacheConstants.EMS_CODE_KEY + email;

        Integer code = ValidateCodeUtil.generateValidateCode(6);
        VsDramasRichtext richtext = new VsDramasRichtext();
        if ("65".equals(langId)) {
            richtext.setTitle("电子邮件模板-中文");
        } else {
            richtext.setTitle("电子邮件模板-英文");
        }
        List<VsDramasRichtext> richtexts = vsDramasRichtextMapper.selectVsDramasRichtextList(richtext);

        VsDramasConfig vsDramasConfig = new VsDramasConfig();
        vsDramasConfig.setName("email");
        List<VsDramasConfig> configs = vsDramasConfigService.selectVsDramasConfigList(vsDramasConfig);
        String value = configs.get(0).getValue();

        Boolean aBoolean = QQMailUtil.sendMail(email, code, richtexts.get(0).getContent(), value);
        if (aBoolean) {
            redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
            return "success";
        } else {
            return "error";
        }
    }

    @Override
    public String sendPhone(String areaCode, String phone, String langId) {
        String verifyKey = CacheConstants.SMS_CODE_KEY + phone;

        Integer code = ValidateCodeUtil.generateValidateCode(6);

        VsDramasConfig vsDramasConfig = new VsDramasConfig();
        vsDramasConfig.setName("sms");
        List<VsDramasConfig> configs = vsDramasConfigService.selectVsDramasConfigList(vsDramasConfig);
        String value = configs.get(0).getValue();
        Map<String, String> map = JSON.parseObject(value, new TypeReference<Map<String, String>>(){});
        String alisms = map.get("alisms");
        Boolean aBoolean = SmsUtil.sendAliYunSms(areaCode+phone, code, langId, alisms);
        // 如果短信发送成功就将key写入缓存等待登录校验
        if (aBoolean) {
            redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
            return "success";
        } else {
            return "error";
        }
    }

    @Override
    public String editPwd(VsUser vsUser) {
        VsUser verify = SecurityUtils.getLoginUser().getVsUser();
        if (!SecurityUtils.matchesPassword(vsUser.getOldpassword(), verify.getPassword())) {
            return "密码不正确！";
        }
        if (SecurityUtils.matchesPassword(vsUser.getNewpassword(), verify.getPassword())) {
            return "新密码不能与旧密码一样！";
        }
        VsUser user = new VsUser();
        user.setId(verify.getId());
        user.setPassword(SecurityUtils.encryptPassword(vsUser.getNewpassword()));
        int i = vsUserMapper.updateVsUser(user);
        if (i > 0) {
            return "修改密码成功！";
        }
        return "修改密码失败！";
    }

    @Override
    public Boolean forgotPassword(VsUser vsUser) {
        VsUser user = new VsUser();

        String regex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(vsUser.getKeyword());
        // 如果符合表达式则代表keyword是邮箱，否则是手机号
        if (matcher.matches()) {
            user.setEmail(vsUser.getKeyword());
        } else {
            user.setMobile(vsUser.getKeyword());
        }

        user.setPassword(SecurityUtils.encryptPassword(vsUser.getPassword()));
        int i = vsUserMapper.forgotPassword(user);
        if (i > 0) {
            return true;
        }
        return false;
    }

    @Override
    public List<VsUser> selectParentList(VsUser vsUser) {
        return vsUserMapper.selectParentList(vsUser);
    }

    @Override
    public Boolean updateUserAvatar(Long id, String avatar) {
        return vsUserMapper.updateUserAvatar(id,avatar) > 0;
    }

    @Override
    public int updateVsUserUsable(VsUser vsUser) {
        return vsUserMapper.updateVsUserUsable(vsUser);
    }

    @Override
    public int updateVsUserMoney(VsUser vsUser) {
        return vsUserMapper.updateVsUserMoney(vsUser);
    }
}
